﻿using Sidvall.Business;
using Sidvall.Data;
using Sidvall.WorkItemManager.Data.Entity.Entities;
using Sidvall.WorkItemManager.Data.Fields;
using Sidvall.WorkItemManager.Workflow;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Sidvall.WorkItemManager.Data.Entity.Model.StorageManagers
{
    public static partial class WorkItemManager
    {
        static partial void OnBeforeUpdateResourceObject(StorageManager storageManager, Dictionary<string, object> ids, IWorkItem item, SaveSettings settings, ObjectParameterCollection objectParameters, DataRowState rowState, WorkItem entityItem, CopySettings copySettings, TaskContext taskContext)
        {
            entityItem.WorkerId = item.WorkerId;
            entityItem.Name = item.Name;
            entityItem.Description = item.Description;
            entityItem.ExecutionOrder = item.ExecutionOrder;
            entityItem.WorkItemTypeId = item.WorkItemTypeId;
            entityItem.WorkItemContext = item.WorkItemContext;
            entityItem.WorkRetryPolicyId = item.WorkRetryPolicyId;
            entityItem.WorkRetryPolicyContext = item.WorkRetryPolicyContext;
            entityItem.LogModeId = (int)item.LogModeId;
            entityItem.LogDayCount = item.LogDayCount;
            entityItem.SensorLogDayCount = item.SensorLogDayCount;
            entityItem.CanceledAction = (int)item.CanceledAction;
            entityItem.FailedAction = (int)item.FailedAction;

            taskContext.Result = DataPortalResult.Ok;
        }
        static partial void OnExecute(StorageManager storageManager, Sidvall.Business.DataPortalContext<Sidvall.Data.CommandCriteria, Sidvall.Data.CommandCriteria> dataPortalContext)
        {
            dataPortalContext.Tasks.Add(OnExecuteTask(storageManager, dataPortalContext));
        }
        private static async Task OnExecuteTask(StorageManager storageManager, Sidvall.Business.DataPortalContext<Sidvall.Data.CommandCriteria, Sidvall.Data.CommandCriteria> dataPortalContext)
        {
            bool result;

            switch (dataPortalContext.OperationContext.CriteriaId)
            {
                case ParameterManager.WorkItemCleanLogParameterName:
                    result = await WorkItemCleanLogAsync(storageManager,
                                dataPortalContext.OperationContext.Parameters.ValueToEnumerableInt64("WorkItemIds"),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableInt32("LogDayCount"),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableInt32("SensorLogDayCount")).ConfigureAwait(false);
                    if (result)
                        dataPortalContext.Result = Sidvall.Business.DataPortalResult.Ok;
                    else
                        dataPortalContext.Result = Sidvall.Business.DataPortalResult.Skipped;
                    break;
                case ParameterManager.WorkItemLogParameterName:
                    result = await WorkItemLogAsync(storageManager,
                                dataPortalContext.OperationContext.Parameters.ValueToNullableInt64(WorkItemField.WorkItemId.ToString()).Value,
                                dataPortalContext.OperationContext.Parameters.ValueToNullableDateTime(WorkItemLogField.ExecutionEndTime.ToString()).Value,
                                dataPortalContext.OperationContext.Parameters.ValueToNullableDateTime(WorkItemLogField.ExecutionStartTime.ToString()).Value,
                                dataPortalContext.OperationContext.Parameters.ValueToString(WorkItemLogField.ResultMessage.ToString(), string.Empty),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableEnum<WorkItemResultType>(WorkItemLogField.WorkItemResultId.ToString()).Value).ConfigureAwait(false);
                    if (result)
                        dataPortalContext.Result = Sidvall.Business.DataPortalResult.Ok;
                    else
                        dataPortalContext.Result = Sidvall.Business.DataPortalResult.Skipped;
                    break;
                case ParameterManager.WorkItemResetParameterName:
                    result = await WorkItemResetAsync(storageManager,
                                dataPortalContext.OperationContext.Parameters.ValueToEnumerableInt64("WorkItemIds"),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableEnum<Sidvall.WorkItemManager.Workflow.WorkItemStatus>("WorkItemStatusId"),
                                dataPortalContext.OperationContext.Parameters.ValueToBoolean("ResetExecution", false),
                                dataPortalContext.OperationContext.Parameters.ValueToBoolean("ResetExecutionContext", false),
                                dataPortalContext.OperationContext.Parameters.ValueToBoolean("ResetRetry", false),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableInt32("FailedExecutionCount"),
                                dataPortalContext.OperationContext.Parameters.ValueToBoolean("ResetExecutionHistory", false),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableBoolean("ForceExecution"),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableBoolean("IsCanceled"),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableBoolean("IsEnabled")).ConfigureAwait(false);
                    if (result)
                        dataPortalContext.Result = Sidvall.Business.DataPortalResult.Ok;
                    else
                        dataPortalContext.Result = Sidvall.Business.DataPortalResult.Skipped;
                    break;
                case ParameterManager.WorkItemUpdateParameterName:
                    result = await WorkItemUpdateAsync(storageManager,
                                dataPortalContext.OperationContext.Parameters.ValueToNullableInt64(WorkItemField.WorkItemId.ToString()).Value,
                                dataPortalContext.OperationContext.Parameters.ValueToNullableEnum<WorkItemStatus>(WorkItemField.WorkItemStatusId.ToString()),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableDateTime(WorkItemField.ExecutionStartTime.ToString()),
                                dataPortalContext.OperationContext.Parameters.ValueToString(WorkItemField.ExecutionContext.ToString(), null),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableDateTime(WorkItemField.ExecutionContextUpdated.ToString()),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableByte(WorkItemField.ExecutionCompletionPercentage.ToString()),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableInt32(WorkItemField.RetryCount.ToString()),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableDateTime(WorkItemField.RetryTime.ToString()),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableInt32(WorkItemField.FailedExecutionCount.ToString()),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableDateTime(WorkItemField.LastExecutionStartTime.ToString()),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableDateTime(WorkItemField.LastExecutionEndTime.ToString()),
                                dataPortalContext.OperationContext.Parameters.ValueToNullableEnum<WorkItemResultType>(WorkItemField.LastWorkItemResultId.ToString()),
                                dataPortalContext.OperationContext.Parameters.ValueToString(WorkItemField.LastResultMessage.ToString(), null),
                                dataPortalContext.OperationContext.Parameters.ValueToBoolean(ParameterManager.SaveExecutionContextParameterName, false)).ConfigureAwait(false);
                    if (result)
                        dataPortalContext.Result = Sidvall.Business.DataPortalResult.Ok;
                    else
                        dataPortalContext.Result = Sidvall.Business.DataPortalResult.Skipped;
                    break;
                default:
                    break;
            }
        }

        #region WorkItemCleanLogAsync

        public static async Task<bool> WorkItemCleanLogAsync(StorageManager storageManager, System.Collections.Generic.IEnumerable<long> workItemIds,
            int? logDayCount, int? sensorLogDayCount)
        {
            System.Linq.IQueryable<WorkItemSensorLog> sensorLogQuery;
            System.Linq.IQueryable<WorkItemLog> logQuery;
            System.DateTime endTime;

            if (logDayCount != null)
            {
                endTime = System.DateTime.UtcNow.AddDays(-logDayCount.Value);
                logQuery = (from o in storageManager.ObjectContext.WorkItemLog
                            where (workItemIds.Contains(o.WorkItemId)) && (o.ExecutionEndTime < endTime)
                            select o);
                storageManager.ObjectContext.WorkItemLog.RemoveRange(logQuery);
            }
            if (sensorLogDayCount != null)
            {
                endTime = System.DateTime.UtcNow.AddDays(-sensorLogDayCount.Value);
                sensorLogQuery = (from o in storageManager.ObjectContext.WorkItemSensorLog
                                  join l in storageManager.ObjectContext.WorkItemSensor on o.WorkItemSensorId equals l.WorkItemSensorId
                                  where (workItemIds.Contains(l.WorkItemId)) && (o.ValueTimestamp < endTime)
                                  select o);
                storageManager.ObjectContext.WorkItemSensorLog.RemoveRange(sensorLogQuery);
            }
            await Sidvall.WorkItemManager.Data.Entity.SystemContext.Current.DataManager.SaveChangesAsync(storageManager.ObjectContext).ConfigureAwait(false);

            return true;
        }

        #endregion
        #region WorkItemLogAsync

        public static async Task<bool> WorkItemLogAsync(StorageManager storageManager, long workItemId, System.DateTime executionEndTime,
            System.DateTime executionStartTime, string resultMessage, WorkItemResultType workItemResultId)
        {
            WorkItemLog item;

            var ids = new System.Collections.Generic.Dictionary<string, object>();
            item = new WorkItemLog()
            {
                WorkItemId = workItemId,
                ExecutionEndTime = executionEndTime,
                ExecutionStartTime = executionStartTime,
                ResultMessage = resultMessage,
                WorkItemResultId = (int)workItemResultId,
            };
            item.WorkItemLogId = (long)await WorkItemLogManager.GetNextIdAsync(global::Sidvall.WorkItemManager.Data.Fields.WorkItemLogField.WorkItemLogId,
                ids, storageManager).ConfigureAwait(false);
            storageManager.ObjectContext.WorkItemLog.Add(item);
            await Sidvall.WorkItemManager.Data.Entity.SystemContext.Current.DataManager.SaveChangesAsync(storageManager.ObjectContext).ConfigureAwait(false);

            return true;
        }

        #endregion
        #region WorkItemResetAsync

        public static async Task<bool> WorkItemResetAsync(StorageManager storageManager, System.Collections.Generic.IEnumerable<long> workItemIds,
            Sidvall.WorkItemManager.Workflow.WorkItemStatus? workItemStatusId, bool resetExecution, bool resetExecutionContext, bool resetRetry,
            int? failedExecutionCount, bool resetExecutionHistory, bool? forceExecution, bool? isCanceled, bool? isEnabled)
        {
            System.Linq.IQueryable<WorkItem> query;

            query = (from o in storageManager.ObjectContext.WorkItem
                     where workItemIds.Contains(o.WorkItemId)
                     select o);
            var items = await Sidvall.WorkItemManager.Data.Entity.SystemContext.Current.DataManager.GetEntityObjectsAsync(query).ConfigureAwait(false);
            if (!items.Any())
                return false;
            foreach (var item in items)
            {
                if (workItemStatusId != null)
                {
                    item.WorkItemStatusId = (int)workItemStatusId.Value;
                }
                if (resetExecution)
                {
                    item.ExecutionStartTime = null;
                    item.ExecutionCompletionPercentage = 0;
                }
                if (resetExecutionContext)
                {
                    item.ExecutionContext = string.Empty;
                    item.ExecutionContextUpdated = null;
                }
                if (resetRetry)
                {
                    item.RetryTime = null;
                    item.RetryCount = 0;
                }
                if (failedExecutionCount != null)
                {
                    item.FailedExecutionCount = failedExecutionCount.Value;
                }
                if (resetExecutionHistory)
                {
                    item.LastExecutionStartTime = null;
                    item.LastExecutionEndTime = null;
                    item.LastWorkItemResultId = null;
                    item.LastResultMessage = string.Empty;
                }
                if (forceExecution != null)
                    item.ForceExecution = forceExecution.Value;
                if (isCanceled != null)
                    item.IsCanceled = isCanceled.Value;
                if (isEnabled != null)
                    item.IsEnabled = isEnabled.Value;
            }
            await Sidvall.WorkItemManager.Data.Entity.SystemContext.Current.DataManager.SaveChangesAsync(storageManager.ObjectContext).ConfigureAwait(false);

            return true;
        }

        #endregion
        #region WorkItemUpdateAsync

        public static async Task<bool> WorkItemUpdateAsync(StorageManager storageManager, long workItemId, Sidvall.WorkItemManager.Workflow.WorkItemStatus? workItemStatus,
            System.DateTime? executionStartTime, string executionContext, System.DateTime? executionContextUpdated, byte? executionCompletionPercentage,
            int? retryCount, System.DateTime? retryTime, int? failedExecutionCount, System.DateTime? lastExecutionStartTime,
            System.DateTime? lastExecutionEndTime, Sidvall.WorkItemManager.Workflow.WorkItemResultType? lastWorkItemResultId, string lastResultMessage,
            bool saveExecutionContext)
        {
            System.Linq.IQueryable<WorkItem> query;
            WorkItem item;

            query = (from o in storageManager.ObjectContext.WorkItem
                     where (o.WorkItemId == workItemId)
                     select o);
            item = await Sidvall.WorkItemManager.Data.Entity.SystemContext.Current.DataManager.GetEntityObjectAsync(query).ConfigureAwait(false);
            if (item == null)
                return false;
            if (!item.IsEnabled)
                return false;

            if (workItemStatus != null)
                item.WorkItemStatusId = (int)workItemStatus.Value;
            item.ExecutionStartTime = executionStartTime;
            if (saveExecutionContext)
            {
                if (executionContext != null)
                    item.ExecutionContext = executionContext;
                item.ExecutionContextUpdated = executionContextUpdated;
                if (executionCompletionPercentage != null)
                    item.ExecutionCompletionPercentage = executionCompletionPercentage.Value;
            }
            if (retryCount != null)
                item.RetryCount = retryCount.Value;
            item.RetryTime = retryTime;
            if (failedExecutionCount != null)
                item.FailedExecutionCount = failedExecutionCount.Value;
            item.LastExecutionStartTime = lastExecutionStartTime;
            item.LastExecutionEndTime = lastExecutionEndTime;
            item.LastWorkItemResultId = (int?)lastWorkItemResultId;
            if (lastResultMessage != null)
                item.LastResultMessage = lastResultMessage;

            await Sidvall.WorkItemManager.Data.Entity.SystemContext.Current.DataManager.SaveChangesAsync(storageManager.ObjectContext).ConfigureAwait(false);

            return true;
        }

        #endregion
    }
}